#include "CXMLFactoryEnvironment.h"
#include <lampe/exception/Null.h>
#include "CXmlNode.h"
#include "Tags/include.h"
#include "Tags/style.h"
#include "Tags/style_item.h"
#include "Tags/using.h"
/*
#include "Tags/style.h"

#include "Tags/style_item.h"
#include "Tags/animation.h"
#include "Tags/animation_item.h"

#include "Tags/billboard.h"
#include "Tags/material.h"
#include "Tags/material__flag.h"
#include "Tags/material_image.h"

#include "Tags/mesh_join.h"
#include "Tags/mesh_skin.h"
#include "Tags/findbyid.h"
#include "Tags/using.h"
#include "Tags/animator.h"
#include "Tags/terrain.h"
*/
#include <lampe/exception/IO.h>
#include <lampe/io/IFileReader.h>
#include <boost/filesystem.hpp>
#include "CXMLSaxList.h"
#include "FactorySimpeXML.h"

#define MARKER printf("%s(%d)\n",__FILE__,__LINE__)

namespace lampe{
namespace xml{

lampe::xml::TagResult CXMLFactoryEnvironment::runNode(IXmlNode*n){
	if (!n){//если узла нет
		GEN_NULL_ERROR("Can't run NULL Node");//то генерируем ошибку
		return lampe::xml::STOP;
	}
	n->grab();//увеличиваем счётчик ссылок на узле

	getFileSystem()->pwd_lock();//блокируем изменение текущего каталога
	boost::filesystem::path old_path=getFileSystem()->pwd();//сохраняем значение текущего каталога

	if (n->getName().size()<=0){//если у узла нет имини
		getFileSystem()->pwd_unlock();//разблокируем файловую систему
		n->drop();//удалим ссылку с узла
		return lampe::xml::STOP;//прекратим исполнение
	}

	//цикл по всем атрибутам
	for (irr::u32 i=0;i<n->getAttributeCount();i++){
		IFactoryRecord*it=getFactoryByAttributeName(n->getAttribute(i)->getName());
		if (it){
			lampe::xml::TagResult res=it->attributeHandler(this,n,i);
			switch(res){
			case lampe::xml::BACK:
				i--;
				break;
			case lampe::xml::STOP:
				getFileSystem()->pwd(old_path);
				getFileSystem()->pwd_unlock();
				return lampe::xml::STOP;
			case lampe::xml::NEXT:
				break;
			}
		}
	}
	IFactoryRecord*it=getFactoryByTagName(n->getName());
	
	lampe::xml::TagResult res=lampe::xml::NEXT;
	if (it){
		res=it->tagHandlerBefor(this,n);
	}
	if (res==lampe::xml::NEXT){
		for (irr::u32 i=0;i<n->getNodeCount();i++){
			lampe::xml::TagResult res2=runNode(n->getNode(i));
			if (res2==lampe::xml::BACK){
				i--;
			}
			if (res2==lampe::xml::STOP){
				break;
			}
		}
	}

	lampe::xml::TagResult res2=lampe::xml::NEXT;
	if (it)
		res2=it->tagHandlerAfter(this,n);
	n->drop();
	getFileSystem()->pwd(old_path);//возвращяем старый путь
	getFileSystem()->pwd_unlock();//разблокируем изменение текущего каталога
	return res2;
}

void CXMLFactoryEnvironment::addTagHandler(String name,IFactoryRecord*handler){
	IFactoryRecord*h=getFactoryByTagName(name);
	if (h)
		return;
	tags.insert(std::pair<String,IFactoryRecord*>(name,handler));
	handler->grab();
}

void CXMLFactoryEnvironment::addAttributeHandler(String name,IFactoryRecord*handler){
	IFactoryRecord*h=getFactoryByAttributeName(name);
	if (h)
		return;
	attributes.insert(std::pair<String,IFactoryRecord*>(name,handler));
	handler->grab();
}

void CXMLFactoryEnvironment::removeHandlerFromTag(String name){
	LAMPE_MAP_VENDOR<String,IFactoryRecord*>::iterator it=tags.find(name);
	if (it==tags.end())
		return;
	(*it).second->drop();
	tags.erase(it);
}

void CXMLFactoryEnvironment::removeHandlerFromAttribute(String name){
	LAMPE_MAP_VENDOR<String,IFactoryRecord*>::iterator it=attributes.find(name);
	if (it==attributes.end())
		return;
	(*it).second->drop();
	attributes.erase(it);
}

void CXMLFactoryEnvironment::registerHandler(IFactoryRecord*record){
	LAMPE_VECTOR_VENDOR<String> t=record->getProcessedTags();
	LAMPE_VECTOR_VENDOR<String> a=record->getProcessedAttributes();
	for (irr::u32 i=0;i<t.size();i++){
		addTagHandler(t[i],record);
	}
		
	for (irr::u32 i=0;i<a.size();i++){
		addAttributeHandler(a[i],record);
	}
}
/*
IrrlichtDevice*CXMLFactoryEnvironment::getDevice(){
	return device;
}
*/

CXMLFactoryEnvironment::CXMLFactoryEnvironment(io::IFileSystem*fs){
	fileSystem=fs;
	//device=d;
	//fileSystem=device->getFileSystem();
	regInclude(this);
	regStyle(this);
	regUsing(this);
	/*
	regStyleItem(this);

	regStyle(this);
	
	regAnimation(this);
	regAnimationItem(this);
	readMaterialFlag(this);
	regMaterial(this);
	regMaterialImage(this);
	regBillboard(this);
	regFindByID(this);
	regMeshSkin(this);
	regMeshJoin(this);
	regAnimator(this);
	regTerrain(this);
	*/
}

CXMLFactoryEnvironment::~CXMLFactoryEnvironment(){
	LAMPE_MAP_VENDOR<String,IFactoryRecord*>::iterator it;
	it=tags.begin();
	if (it!=tags.end())
		do{
			(*it).second->drop();
			it++;
		}while(it!=tags.end());
	tags.clear();

	it=attributes.begin();
	if (it!=attributes.end())
		do{
			(*it).second->drop();
			it++;
		}while(it!=attributes.end());
	attributes.clear();
}

IFactoryRecord*CXMLFactoryEnvironment::getFactoryByTagName(String name){
	LAMPE_MAP_VENDOR<String,IFactoryRecord*>::iterator it=tags.find(name);
	if (it==tags.end())
		return 0;
	return (*it).second;
}
IFactoryRecord*CXMLFactoryEnvironment::getFactoryByAttributeName(String name){
	LAMPE_MAP_VENDOR<String,IFactoryRecord*>::iterator it=attributes.find(name);
	if (it==attributes.end())
		return 0;
	return (*it).second;
}

IXmlNode*CXMLFactoryEnvironment::createXmlTree(boost::filesystem::path fileName){
	stream::IReader*file=fileSystem->read(fileName);
	IXmlNode*root=createXmlTree(file);
	file->drop();
	return root;
}

IXmlNode*CXMLFactoryEnvironment::createXmlTree(stream::IReader*file){
	CXMLSaxList*list=new CXMLSaxList(file);
	IXmlNode*root=new CXmlNode(list,"");
	list->drop();
	return root;
}

void CXMLFactoryEnvironment::regSimpeTag(lampe::String element,XMLTagFactoryFunction function){
	if (!function)
		return;
	FactorySimpeXML*fs=new FactorySimpeXML(element,function);
	registerHandler(fs);
	fs->drop();
}
void CXMLFactoryEnvironment::regSimpeAttribute(lampe::String element,XMLAttribyteFactoryFunction function){
	if (!function)
		return;
	FactorySimpeXML*fs=new FactorySimpeXML(element,function);
	registerHandler(fs);
	fs->drop();
}

lampe::io::IFileSystem*CXMLFactoryEnvironment::getFileSystem(){
	return fileSystem;
}

/*
IXmlNode*CXMLFactoryEnvironment::getXmlTree(String fileName){
	//fileSystem->lock();
	if (!fileSystem->existFile(fileName)){
		//LOGE("File \"%s\" not found!WorkDir=%s",filename.c_str(),boost::filesystem::current_path().c_str());
		//fileSystem->unlock();
		GEN_IO_ERROR(String(String("Xml file \"")+fileName+"\" not found!"));
		return 0;
	}
	irr::io::IReadFile*file=fileSystem->createAndOpenFile(fileName);
	if (!file){
		//LOGE("Can't get file \"%s\"",filename.c_str());
		//fileSystem->unlock();
		GEN_IO_ERROR(String(String("Can't get file \"")+fileName+"\""));
		return 0;
	}
	
	IXmlNode*res=getXmlTree(file,fileName);

	
	file->drop();
	//fileSystem->unlock();
	return res;
}

IXmlNode*CXMLFactoryEnvironment::getXmlTree(irr::io::IReadFile*stream,String fileName){
	irr::io::IXMLReaderUTF8*reader=(irr::io::IXMLReaderUTF8*)fileSystem->createXMLReaderUTF8(stream);
	reader->read();
	reader->read();
	IXmlNode*res=lampe::xml::createXmlNode(reader,fileSystem->getFileDir(fileSystem->getAbsolutePath(fileName)),0);
	reader->drop();
	return res;
}
*/

IXMLFactoryEnvironment*createXMLFactoryEnvironment(io::IFileSystem*fileSystem){
	return new CXMLFactoryEnvironment(fileSystem);
}

}//namespace xml
}//namespace lampe
